package com.hclc.yxb.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hclc.config.util.AssertUtil;
import com.hclc.entity.dvo.MenuTreeMetaDvo;
import com.hclc.entity.model.SysUserModel;
import com.hclc.entity.model.contans.ApiConst;
import com.hclc.entity.model.enums.AdminStatusEnum;
import com.hclc.entity.model.enums.BooleanTypeEnum;
import com.hclc.entity.model.enums.HiddenEnum;
import com.hclc.entity.model.enums.PermTypeEnum;
import com.hclc.util.bean.BeanCopyUtils;
import com.hclc.util.excption.BusinessException;
import com.hclc.util.json.JsonUtil;
import com.hclc.yxb.ao.AddRoleAndMenuAo;
import com.hclc.yxb.ao.ModRoleAndMenuAo;
import com.hclc.yxb.ao.ModSystemRoleMenuAo;
import com.hclc.yxb.dto.RoleAndMenuDto;
import com.hclc.yxb.dto.RoleAndMenuListDto;
import com.hclc.yxb.dto.SystemMenuDto;
import com.hclc.yxb.mapper.*;
import com.hclc.yxb.model.*;
import com.hclc.yxb.service.RoleManagementService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassName PwlRoleManagementServiceImpl
 * @Author: yurj
 * @Mail：1638234804@qq.com
 * @Date: Create in 9:01 2020/8/23
 * @version: 1.0
 */
@Service
@Slf4j
public class RoleManagementServiceImpl implements RoleManagementService {

    @Autowired
    private SysRoleMapper sysRoleMapper;

    @Autowired
    private SysRoleMenuMapper sysRoleMenuMapper;

    @Autowired
    private SysMenuMapper sysMenuMapper;

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;

    @Autowired
    private SysRoleMenuModelBatchServiceImpl sysRoleMenuModelBatchService;

    /**
     * 获取权限和菜单列表
     *
     * @param page
     * @return com.baomidou.mybatisplus.core.metadata.IPage<com.hclc.pwl.dto.RoleAndMenuDto>
     * @author yurj
     * @mail 1638234804@qq.com
     * @version 1.0
     * @date 2020/8/23 9:36
     */
    @Override
    public IPage<RoleAndMenuDto> selectRoleAndMenu(Page<SysRoleModel> page) throws BusinessException {
        // 超级管理员
        SysUserModel sysUserModel = sysUserMapper.selectOne(new QueryWrapper<SysUserModel>()
                .lambda()
                .eq(SysUserModel::getEnable, ApiConst.ENABLE_TRUE)
                .eq(SysUserModel::getAdminStatus, AdminStatusEnum.YES)
        );
        // 超级管理员角色权限
        SysUserRoleModel sysUserRoleModel = sysUserRoleMapper.selectOne(new QueryWrapper<SysUserRoleModel>()
                .lambda()
                .eq(SysUserRoleModel::getEnable, ApiConst.ENABLE_TRUE)
                .eq(SysUserRoleModel::getUserId, sysUserModel.getId())
        );

        Page<SysRoleModel> dataPage = sysRoleMapper.selectPage(page, new QueryWrapper<SysRoleModel>()
                .lambda()
                .eq(SysRoleModel::getEnable, ApiConst.ENABLE_TRUE)
                // 排除超级管理员角色
                .ne(SysRoleModel::getId, sysUserRoleModel.getRoleId())
                .orderByDesc(SysRoleModel::getCreateTime)
        );
        IPage<RoleAndMenuDto> resultPage = new Page<>(dataPage.getCurrent(), dataPage.getSize(), dataPage.getTotal());
        if (CollectionUtils.isEmpty(dataPage.getRecords())) {
            return resultPage;
        }
        // 角色map
        Map<String, SysRoleModel> roleMap = dataPage.getRecords().parallelStream().collect(Collectors.toMap(s -> s.getId(), s -> s));
        // 角色id集
        List<String> roleIds = dataPage.getRecords().parallelStream().map(s -> s.getId()).collect(Collectors.toList());
        // 角色菜单表
        List<SysRoleMenuModel> sysRoleMenuModels = sysRoleMenuMapper.selectList(new QueryWrapper<SysRoleMenuModel>()
                .lambda()
                .eq(SysRoleMenuModel::getEnable, ApiConst.ENABLE_TRUE)
                .in(SysRoleMenuModel::getRoleId, roleIds)
        );
        // 角色菜单对应关系
        Map<String, Set<String>> roleMenuMap = new HashMap<>(ApiConst.DEFAULT_INITIAL_CAPACITY);
        // 所有菜单id
        Set<String> menuIds = new HashSet<>();
        // 初始化
        roleIds.stream().forEach(s -> {
            roleMenuMap.put(s, new HashSet<>());
        });
        // 分类获取每个角色对应的菜单id集
        sysRoleMenuModels.stream().forEach(s -> {
            Set<String> currentRoleMenuIds = roleMenuMap.get(s.getRoleId());
            menuIds.add(s.getMenuId());
            currentRoleMenuIds.add(s.getMenuId());
            roleMenuMap.put(s.getRoleId(), currentRoleMenuIds);
        });
        menuIds.add(ApiConst.ZERO_STRING);
        // 所有菜单信息
        Map<String, SysMenuModel> menuMap = sysMenuMapper.selectList(new QueryWrapper<SysMenuModel>()
                .lambda()
                .eq(SysMenuModel::getEnable, ApiConst.ENABLE_TRUE)
                .in(SysMenuModel::getId, menuIds)
        ).parallelStream().collect(Collectors.toMap(s -> s.getId(), s -> s));
        // 角色菜单dto
        List<RoleAndMenuDto> dtoList = new ArrayList<>();
        // 循环角色菜单，组装信息
        roleMenuMap.forEach((k, v) -> {
            // 当前角色
            RoleAndMenuDto dto = new RoleAndMenuDto();
            dto.setRoleId(StringUtils.EMPTY);
            dto.setRoleName(StringUtils.EMPTY);
            dto.setRoleDesc(StringUtils.EMPTY);
            dto.setCheckAllStatus(false);
            Optional.ofNullable(roleMap.get(k)).ifPresent(role -> {
                dto.setRoleId(role.getId());
                dto.setRoleName(role.getRoleName());
                dto.setRoleDesc(role.getRoleDesc());
            });
            dto.setMenuList(new ArrayList<>());
            // 角色是否有菜单
            if (!CollectionUtils.isEmpty(v)) {
                v.forEach(menuId -> {
                    SystemMenuDto menuDto = new SystemMenuDto();
                    SysMenuModel sysMenuModel = menuMap.get(menuId);
                    try {
                        BeanUtils.copyProperties(sysMenuModel, menuDto);
                    } catch (BeansException e) {
                        log.error("获取角色对应菜单赋值发生异常,source:{},target:{}", sysMenuModel, menuDto, e);
                    }
                    menuDto.setHidden(HiddenEnum.YES.equals(sysMenuModel.getHidden()) ? true : false);
                    if (StringUtils.isNotBlank(sysMenuModel.getMeta())) {
                        menuDto.setMeta(JsonUtil.toObject(sysMenuModel.getMeta(), MenuTreeMetaDvo.class));
                    }
                    dto.getMenuList().add(menuDto);
                });
            }
            dtoList.add(dto);
        });

        return resultPage.setRecords(dtoList);
    }

    /**
     * 获取角色下拉
     *
     * @param
     * @return java.util.List<com.hclc.hxwy.console.dto.RoleAndMenuListDto>
     * @author ljj
     * @version 1.0
     * @date 2020/10/30 11:26
     */
    @Override
    public List<RoleAndMenuListDto> selectRoleAndMenuList() throws BusinessException {
        // 超级管理员
        SysUserModel sysUserModel = sysUserMapper.selectOne(new QueryWrapper<SysUserModel>()
                .lambda()
                .eq(SysUserModel::getEnable, ApiConst.ENABLE_TRUE)
                .eq(SysUserModel::getAdminStatus, AdminStatusEnum.YES)
        );
        // 超级管理员角色权限
        SysUserRoleModel sysUserRoleModel = sysUserRoleMapper.selectOne(new QueryWrapper<SysUserRoleModel>()
                .lambda()
                .eq(SysUserRoleModel::getEnable, ApiConst.ENABLE_TRUE)
                .eq(SysUserRoleModel::getUserId, sysUserModel.getId())
        );

        List<SysRoleModel> sysRoleModels = sysRoleMapper.selectList(new QueryWrapper<SysRoleModel>()
                .lambda()
                .eq(SysRoleModel::getEnable, ApiConst.ENABLE_TRUE)
                // 排除超级管理员角色
                .ne(SysRoleModel::getId, sysUserRoleModel.getRoleId())
                .orderByDesc(SysRoleModel::getCreateTime)
        );

        List<RoleAndMenuListDto> retList = new ArrayList<>(sysRoleModels.size());
        sysRoleModels.stream().forEach(s -> {
            RoleAndMenuListDto roleAndMenuListDto = roleAndMenuListDto();
            copy(s, roleAndMenuListDto);
            retList.add(roleAndMenuListDto);
        });
        return retList;
    }

    /**
     * CV大法好
     *
     * @param s
     * @param object
     * @return void
     * @author ljj
     * @version 1.0
     * @date 2020/8/9 14:55
     */
    private void copy(Object s, Object object) {
        try {
            BeanCopyUtils.copyProperties(s, object);
        } catch (Exception e) {
            log.error("", e);
            return;
        }
    }

    /**
     * 添加角色
     *
     * @param ao
     * @return boolean
     * @author yurj
     * @mail 1638234804@qq.com
     * @version 1.0
     * @date 2020/8/23 10:58
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insertSystemRole(AddRoleAndMenuAo ao) throws BusinessException {
        SysRoleModel sysRoleModel = new SysRoleModel();
        sysRoleModel.setRoleDesc(ao.getRoleDesc());
        sysRoleModel.setRoleName(ao.getRoleName());
        BooleanTypeEnum booleanTypeEnum = ao.getIsOnlineService() ? BooleanTypeEnum.YES : BooleanTypeEnum.NO;
        int sysRoleInsert = sysRoleMapper.insert(sysRoleModel);
        AssertUtil.isTrue(sysRoleInsert > 0, () -> "添加角色失败");
        return true;
    }

    /**
     * 删除角色和权限
     *
     * @param roleId
     * @return boolean
     * @author yurj
     * @mail 1638234804@qq.com
     * @version 1.0
     * @date 2020/8/23 11:13
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteSystemRole(String roleId) throws BusinessException {
        sysRoleMenuMapper.update(new SysRoleMenuModel(), new UpdateWrapper<SysRoleMenuModel>()
                .lambda()
                .eq(SysRoleMenuModel::getEnable, ApiConst.ENABLE_TRUE)
                .eq(SysRoleMenuModel::getRoleId, roleId)
                .set(SysRoleMenuModel::getEnable, ApiConst.ENABLE_FALSE)
        );
        int userUpdate = sysRoleMapper.update(new SysRoleModel(), new UpdateWrapper<SysRoleModel>()
                .lambda()
                .eq(SysRoleModel::getEnable, ApiConst.ENABLE_TRUE)
                .eq(SysRoleModel::getId, roleId)
                .set(SysRoleModel::getEnable, ApiConst.ENABLE_FALSE)
        );
        AssertUtil.isTrue(userUpdate > 0, () -> "删除角色和权限失败");
        return true;
    }

    /**
     * 修改角色
     *
     * @param ao
     * @return boolean
     * @author yurj
     * @mail 1638234804@qq.com
     * @version 1.0
     * @date 2020/8/23 11:19
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateSystemRole(ModRoleAndMenuAo ao) throws BusinessException {
        SysRoleModel sysRoleModel = new SysRoleModel();
        sysRoleModel.setRoleName(ao.getRoleName());
        sysRoleModel.setRoleDesc(ao.getRoleDesc());
        BooleanTypeEnum booleanTypeEnum = ao.getIsOnlineService() ? BooleanTypeEnum.YES : BooleanTypeEnum.NO;
        int sysRoleUpdate = sysRoleMapper.update(sysRoleModel, new UpdateWrapper<SysRoleModel>()
                .lambda()
                .eq(SysRoleModel::getEnable, ApiConst.ENABLE_TRUE)
                .eq(SysRoleModel::getId, ao.getRoleId())
        );
        AssertUtil.isTrue(sysRoleUpdate > 0, () -> "修改角色失败");
        return true;
    }

    /**
     * 修改角色菜单列表
     *
     * @param ao
     * @return boolean
     * @author yurj
     * @mail 1638234804@qq.com
     * @version 1.0
     * @date 2020/8/23 14:56
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateSystemRoleMenu(ModSystemRoleMenuAo ao) throws BusinessException {
        sysRoleMapper.update(new SysRoleModel(), new UpdateWrapper<SysRoleModel>()
                .lambda()
                .eq(SysRoleModel::getId, ao.getRoleId())
                .set(SysRoleModel::getPermType, ao.getCheckAllStatus() ? PermTypeEnum.ALL : PermTypeEnum.SECTION)
        );
        // 菜单集合
        Map<String, SysMenuModel> menuMap = sysMenuMapper.selectBatchIds(ao.getMenuIds()).parallelStream().collect(Collectors.toMap(s -> s.getId(), s -> s, (v1, v2) -> v2));
        sysRoleMenuMapper.update(new SysRoleMenuModel(), new UpdateWrapper<SysRoleMenuModel>()
                .lambda()
                .eq(SysRoleMenuModel::getEnable, ApiConst.ENABLE_TRUE)
                .eq(SysRoleMenuModel::getRoleId, ao.getRoleId())
                .set(SysRoleMenuModel::getEnable, ApiConst.ENABLE_FALSE)
        );
        // 角色菜单表
        List<SysRoleMenuModel> sysRoleMenuModels = new ArrayList<>();
        ao.getMenuIds().stream().forEach(s -> {
            SysMenuModel sysMenuModel = menuMap.get(s);
            AssertUtil.notNull(sysMenuModel, () -> "没有查找到该菜单信息,ID:" + s);
            SysRoleMenuModel sysRoleMenuModel = new SysRoleMenuModel();
            sysRoleMenuModel.setRoleId(ao.getRoleId());
            sysRoleMenuModel.setMenuId(s);
            sysRoleMenuModels.add(sysRoleMenuModel);
        });
        boolean saveStatus = sysRoleMenuModelBatchService.saveBatch(sysRoleMenuModels);
        AssertUtil.isTrue(saveStatus, () -> "添加菜单失败");
        return true;
    }

    /**
     * 查询菜单
     *
     * @param
     * @return java.util.List<com.hclc.pwl.dto.SystemMenuDto>
     * @author yurj
     * @mail 1638234804@qq.com
     * @version 1.0
     * @date 2020/8/23 13:25
     */
    @Override
    public List<SystemMenuDto> selectSystemMenuList() throws BusinessException {
        List<SysMenuModel> sysMenuModels = sysMenuMapper.selectList(new QueryWrapper<SysMenuModel>()
                .lambda()
                .eq(SysMenuModel::getEnable, ApiConst.ENABLE_TRUE)
                // 非超级管理员菜单
                .eq(SysMenuModel::getAdminStatus, AdminStatusEnum.NO)
        );
        List<SystemMenuDto> dtoList = new ArrayList<>();
        sysMenuModels.stream().forEach(s -> {
            SystemMenuDto systemMenuDto = new SystemMenuDto();
            try {
                BeanUtils.copyProperties(s, systemMenuDto);
            } catch (BeansException e) {
                log.error("获取角色对应菜单赋值发生异常,source:{},target:{}", s, systemMenuDto, e);
            }
            systemMenuDto.setHidden(HiddenEnum.YES.equals(s.getHidden()) ? true : false);
            if (StringUtils.isNotBlank(s.getMeta())) {
                systemMenuDto.setMeta(JsonUtil.toObject(s.getMeta(), MenuTreeMetaDvo.class));
            }
            dtoList.add(systemMenuDto);
        });
        return dtoList;
    }


    /**
     * 初始化{RoleAndMenuListDto}
     *
     * @param
     * @return com.hclc.hxwy.console.dto.RoleAndMenuListDto
     * @author ljj
     * @version 1.0
     * @date 2020/10/30 11:30
     */
    private RoleAndMenuListDto roleAndMenuListDto() {
        RoleAndMenuListDto dto = new RoleAndMenuListDto();
        dto.setId(Strings.EMPTY);
        dto.setRoleName(Strings.EMPTY);
        dto.setRoleDesc(Strings.EMPTY);
        return dto;
    }
}
